<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
	<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
	<TITLE></TITLE>
	<META NAME="GENERATOR" CONTENT="OpenOffice 4.1.1  (Unix)">
	<META NAME="CREATED" CONTENT="0;0">
	<META NAME="CHANGEDBY" CONTENT="Alexandre Gauthier">
	<META NAME="CHANGED" CONTENT="20150207;11014200">
	<META NAME="CocoaVersion" CONTENT="1187.4">
	<META NAME="CHANGEDBY" CONTENT="Alexandre Gauthier">
	<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
	<STYLE TYPE="text/css">
	<!--
		P.p1 { margin-bottom: 0cm; font-family: "Helvetica"; font-size: 11pt }
		P.p2 { margin-bottom: 0cm; font-family: "Helvetica"; font-size: 7pt }
		P.p3 { margin-bottom: 0cm; font-family: "Helvetica"; font-size: 6pt }
		P.p4 { margin-bottom: 0cm; font-family: "Helvetica"; font-size: 6pt }
		P.p6 { margin-bottom: 0cm; font-family: "Helvetica"; font-size: 11pt }
		P.p7 { margin-bottom: 0cm; font-family: "Helvetica"; font-size: 7pt }
		P.p8 { margin-left: 1.11cm; text-indent: -1.11cm; margin-bottom: 0cm; font-family: "Helvetica"; font-size: 7pt }
		P.p9 { margin-bottom: 0cm; color: #0000ee; font-family: "Helvetica"; font-size: 7pt }
		P.p10 { margin-bottom: 0cm; font-family: "Helvetica"; font-size: 9pt }
		P.p11 { margin-bottom: 0cm; font-family: "Helvetica"; font-size: 9pt }
		P.p12 { margin-bottom: 0cm; color: #ffffff; font-family: "Menlo"; font-size: 7pt }
		P.p13 { margin-bottom: 0cm; font-family: "Menlo"; font-size: 7pt }
		P.p14 { margin-bottom: 0cm; color: #323333; font-family: "Menlo"; font-size: 7pt }
		P.p15 { margin-bottom: 0cm; font-family: "Menlo"; font-size: 7pt; text-align: right }
		P.p16 { margin-bottom: 0cm; font-family: "Helvetica"; font-size: 8pt }
		P.p17 { margin-bottom: 0cm; font-family: "Menlo"; font-size: 7pt }
		P.p20 { margin-bottom: 0.44cm; font-family: "Times"; font-size: 7pt }
		P.p21 { margin-bottom: 0.37cm; font-family: "Times"; font-size: 7pt }
		P.p5 { margin-bottom: 0cm; font-family: "Helvetica"; font-size: 9pt }
	-->
	</STYLE>
</HEAD>
<BODY LANG="en-GB" DIR="LTR">
<P CLASS="p1" STYLE="margin-bottom: 0.5cm"><B>OpenFX plug-in
programming guide (C/C++)</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<UL>
	<LI><P CLASS="p3" STYLE="margin-bottom: 0.5cm"><I>1st draft
	08/24/2014 </I>Alexandre Gauthier-Foichat</P>
	<LI><P CLASS="p3" STYLE="margin-bottom: 0.5cm"><I>Refined with
	Pierre Jasmin notes on 02/07/2014</I></P>
</UL>
<P CLASS="p4" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p4" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p1" STYLE="margin-bottom: 0.5cm"><I><B>Table of contents</B></I></P>
<P CLASS="p4" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<OL>
	<LI><P STYLE="margin-bottom: 0cm">Introduction&nbsp; 
	</P>
	<LI><P STYLE="margin-bottom: 0cm">Presentation of the Support layer 
	</P>
	<LI><P>Walkthrough : The Invert plug-in 
	</P>
</OL>
<P CLASS="p4" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>Introduction</B></P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This guide is intended for
confirmed C++ developers who want to develop OpenFX plug-ins using
the Natron fork of the OpenFX standard:
<A HREF="https://github.com/NatronGitHub/openfx">https://github.com/NatronGitHub/openfx</A>.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Note that the Natron fork
is 100% compliant with the original repository and using this guide
ensures that your plug-in(s) will work across all hosts. 
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">First off, the main
<I>official</I> specification can be found right there
<A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html">http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html</A>
and should be followed very precisely when in case of doubt. This
document is 100% true and answers almost all questions one might
have. However this is a long document which can be quite hard to
swallow the first time one has to read it.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">I'll skip all the details
regarding packaging of OpenFX pug-ins and the philosophy and focus
this document on the programming of OpenFX plug-ins using the <B>Support</B>
layer.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">What is the <B>Support</B>
layer?</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">OpenFX is in under the
hood just a protocol for a plug-in and a host application to help
communicate.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">They do so with a C API
using <I>blind</I> <I>handles</I> and properties which are uniquely
identified by names, which all begins with kOfx* . The reason why C
language is used is because the C++ API is sensitive to compiler
versions.<SPAN STYLE="background: #ffffff">A plugin compiled using
the base C API will work fine in Natron, this is about documenting
the C++ wrapper to develop plugin for Natron.</SPAN></P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">To help using this API,
the official guys from the OpenFX association have made a C++ wrapper
around it so it is easier to use. The wrapper on the plug-in side is
called the <B>Support</B> layer and the one on the Host side (the
application, e.g. Natron) is called the <B>HostSupport</B> layer.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The official repository of
the OpenFX association is actively maintained though their C++ layers
contain bugs and</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">missing implementations .
That's why the Natron dev team has forked it and continue maintaining
it and fixing bugs whilst also incorporating new features of the
newer versions of OpenFX.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The official repository of
the OpenFX association can be found here:
<A HREF="https://github.com/ofxa/openfx">https://github.com/ofxa/openfx</A></P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The repository the Natron
dev team maintains can be found here:
<A HREF="https://github.com/NatronGitHub/openfx">https://github.com/NatronGitHub/openfx</A></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">On this repository you
will find the 2 laye`rs mentioned above in separate folders with
their respective names.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The Examples directory
contains OpenFX plug-in examples which were programmed using directly
the C API (thus not using the <B>Support</B> layer).</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The <B>Support</B> folder
contains a Plugins folder which contains other examples. These
examples are on the other hand programmed using the <B>Support</B>
layer.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The Natron dev team has 2
separate repositories for its OpenFX plug-ins.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">One is to handle all
plug-ins which do input/output operations and rely on external
libraries (such as OpenImageIO, OpenColorIO,FFMPEG,OpenEXR, etc…)
and can be found here: <A HREF="https://github.com/NatronGitHub/openfx-io">https://github.com/NatronGitHub/openfx-io</A></P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">The other one is for all
other plug-ins which do image processing. They do not require linking
to any other external library and are generally easier to compile and
understand. The address of that repository is:
<A HREF="https://github.com/NatronGitHub/openfx-misc">https://github.com/NatronGitHub/openfx-misc</A></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Depending on what kind of
plug-in you implement you should base your work upon one of the 2
repositories above as they contain &quot;state of the art&quot;
OpenFX plug-ins. They use the OpenFX API as one should do.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">If you were to create a
new reader plug-in to read whatever you need that isn't supported
already, I suggest that you fork openfx-io and derive the
GenericReader class which does all the quite complex handling of what
a fully-featured reader plug-in is expected to do (such as
downscaling, color-space transformation) . Same for writer plug-ins,
I suggest you derive the GenericWriter class.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm; background: #ffffff">Note
that originally, Readers and Writers are not part of the OpenFX
standard, and to implement the I/O plug-ins, Natron is using the
TuttleOFX Reader/Writer context extension<SPAN STYLE="background: #ffffff">
. As we will see later a plugin specifies in which context it can
run.</SPAN></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">If on the other hand you
were to write an image processing plug-in, I suggest you fork the
openfx-misc repository and look at the plug-ins inside as examples.
The Crop and Invert plug-ins are trivial plug-ins which should give
you a fair understanding on how OpenFX works.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>Architecture of a
plug-in</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">A plug-in is a folder on
Windows/Linux and a bundle on MacOSX which can be represented as
follows:</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">MyPlugin.ofx.bundle/</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Contents/</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Win32/</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">MyPlugin.ofx</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Win64/</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">MyPlugin.ofx</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Resource/</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">MyPlugin.svg</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The .ofx is just a .so on
Linux, a .dylib on MacOSX and a .dll on Windows whose extension has
been renamed to .ofx.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">For an extensive
explanation of the details of how the bundle should be setup, please
refer to</P>
<P CLASS="p9" STYLE="margin-bottom: 0.5cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#id449875">http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#id449875</A></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>Communication between
the host and a plug-in</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Each plug-in can be
instantiated in different context by an application depending on its
use.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">They are well described in
the OpenFX specification and won't go through them here.</P>
<P CLASS="p9" STYLE="margin-bottom: 0.5cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#ImageEffectContexts">http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#ImageEffectContexts</A></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The OpenFX spec. defines
functions that the host can call on the plug-ins to make it perform
special actions. These functions are called <B>actions</B> and their
specification is well described here:</P>
<P CLASS="p9" STYLE="margin-bottom: 0.5cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#id473661">http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#id473661</A></P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The most important action
to implement in general is the render action which is called when a
plug-in needs to render its image. We will detail a bit more the
important actions in the chapter dedicated to the plug-in object.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The plug-in can also call
some functions on the host to query some information. These
functions are grouped in &quot;<I>suites</I>&quot;. Generally the
type of things you would like to ask is &quot;fetch that image&quot;,
&quot;fetch that image's size&quot;, etc…</P>
<P CLASS="p5" STYLE="margin-bottom: 0.5cm"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="background: #ffffff">The
idea of suite is to facilitate evolution, Over time a suite can be
versioned and a version supported from an API version.. As such the
Support Layer matches an API version. </SPAN></FONT>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>Using the C++ Support
Layer</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">A C++ plug-in is composed
of 3 objects in the general cases:</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">The <I><B>factory</B></I>
object which is used to instantiate the plug-in to the host
application at the moment the binary is loaded.</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">The <I><B>plug-in</B></I>
object which is used to communicate with the host application and do
some work. Generally the processing is not done in this object,
rather we do it in the last object…</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">The <I><B>processor</B></I>
object which is used to do the processing in an optimised way: OpenFX
offers a way to do multi-threading easily using this class.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The last class is not
mandatory and one could also do the rendering in the plug-in class,
though it would not be multi-threaded.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>The factory</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">It basically serves 3
purposes:</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">Instantiating the <I><B>plug-in</B></I>
object.</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">Declaring some
parameters/clips: This is done in the <B>describeInContext</B>
function. The parameters are what the user *could* (generally can,
but not if they are hidden/disabled) interact with in the user
interface. They are several types of parameters and they are quite
well described in the specification:</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#ParametersChapter">http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#ParametersChapter</A>
. What you can control is whether the user can animate them, their
name, default value, etc… Please check out the examples in the
openfx-misc for implementation details.</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">The clips are the objects
that refer the input images (in Natron a clip is the arrow between 2
nodes). This is a view of the plug-in of another input plug-in. The
plug-in must always define an output clip: This is where the output
image of this plug-in will be defined.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">Declaring some properties
of the plug-in to the host application. This is done in the <B>describe</B>
function. This is where the plug-in defines its name, which bit
depths it can support,, whether it can support multi-resolution
images, whether it will need fetching images at different times,
whether it supports interlaced images, whether it supports
multi-threading…. etc</P>
<P CLASS="p9" STYLE="margin-bottom: 0.5cm">See the invert plug-in
describe function here
<A HREF="https://github.com/NatronGitHub/openfx-misc/blob/master/Invert/Invert.cpp">https://github.com/NatronGitHub/openfx-misc/blob/master/Invert/Invert.cpp</A></P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">for an example of the
function.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">All properties defined in
the <B>describe</B> function are well covered by the OpenFX
specification though some need some extra caution:</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">-
<A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPluginRenderThreadSafety">kOfxImageEffectPluginRenderThreadSafety</A>
: This must be carefully set. If misused, then your plug-in might not
be thread-safe.</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">Unsafe means that all
instances of the same plug-in can only have 1 render-thread at once:
they will all be synchronised.</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">InstanceSafe means that an
instance can only have 1 render thread at a time, though several
instances do not need special synchronisation.</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">FullySafe indicating that
any instance of a plug-in can have multiple render threads running
simultaneously.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">On top of that the plug-in
can also set the property <A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPluginPropHostFrameThreading">kOfxImageEffectPluginPropHostFrameThreading</A>
: When set to 1, and if the thread-safety of the plug-in permits, the
host will slice the render window and call it with several threads
instead of calling the render function with only 1 thread.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Bear in mind that you as a
plug-in do not need to set this to 1 since you can use the
multi-thread suite and do the multi-threading yourself. This suite
also includes locking abilities allowing you to properly control the
thread-safety of your plug-in.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">-
<A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropSupportsMultiResolution">kOfxImageEffectPropSupportsMultiResolution</A>
: When set to 1 that means your plug-in is expected to work with
arbitrary image rectangles in input and output and they may not
necessarily be the same.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">-
<A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropSupportsTiles">kOfxImageEffectPropSupportsTiles</A>
: When set to 1 that means your plug-in is expected to work with
images that are not the &quot;full image&quot; but just a
sub-rectangle of the full image. <SPAN STYLE="background: #ffffff">Any
per-pixel process is a candidate for this</SPAN></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="background: #ffffff">The
following behavior is expected:</SPAN></FONT></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="background: #ffffff">1)
If a plugin returns an error during <FONT COLOR="#000000"><SPAN LANG="zxx">kOfxActionLoad
it should prior to returning clean up any global memory it will have
allocated using the OFX memory suite. The plugin will then not show
in the UI.</SPAN></FONT></SPAN></FONT></P>
<P LANG="zxx" CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT COLOR="#000000"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="background: #ffffff">2)
If a plugin returns an error during Describe it expects the host to
call kOfxActionUnload and the plugin will not show up in the UI.</SPAN></FONT></FONT></P>
<P LANG="zxx" CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT COLOR="#000000"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="background: #ffffff">3)
In all other cases plugin should release everything during the
kOfxActionUnload. Otherwise during the system dynamic library
destruction the OFX handles will probably be gone.</SPAN></FONT></FONT></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT SIZE=1 STYLE="font-size: 8pt">To
create a group of plug-ins that will all show up under the same menu
in the user interface, you need to set the
<FONT COLOR="#c67c48"><FONT FACE="Menlo-Regular">kOfxImageEffectPluginPropGrouping</FONT></FONT><FONT COLOR="#c67c48">
</FONT><FONT COLOR="#000000"><FONT FACE="Menlo-Regular">property that
is a string containing the menu group under which you want your
plug-in appear, e.g. “Transform” or “Color/Transform”</FONT></FONT></FONT></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>The plug-in object</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This is the main object
allowing to bridge with the application. When implementing your
plug-in you will want to derive the OFX::ImageEffect class. There are
a bunch of virtual functions which are the <B>actions</B> the host
application can call. (The functions I mentioned above).</P>
<P CLASS="p5" STYLE="margin-bottom: 0.5cm"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="background: #ffffff">All
communication occurs via an action handler which the mainEntryStr
function in ofxsImageEffect.cpp.</SPAN></FONT></P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The constructor of the
plug-in is here to fetch a pointer to all the parameters/clip you
defined previously in the <B>describe</B> function of the factory.
These pointers represent the &quot;instantiated&quot; version of
those parameters/clip, whereas in the <B>describe</B> function
well…you just <B>described</B> them so the host would instantiate
them correctly. This is with those pointers that you can get/set
values and query information.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The output clip (sometimes
called dstClip_ in our examples) represents the output image.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The source clip represents
the source image and this is from this object that you need to fetch
the input image.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">If you were to have
several input clips, then you would fetch the input images from each
of your source clips.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Some clips can be optional
(such as a mask for example) and needs to be set so explicitly in the
describe function of the factory.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In the following I will
vaguely re-explain the main actions that are generally implemented by
a plug-in. Though for more detailed explanation and how to report
errors in these functions please check the OpenFX specification which
does a full coverage on them.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>The isIdentity action</B></P>
<P CLASS="p10" STYLE="margin-bottom: 0.5cm">virtual bool
isIdentity(const IsIdentityArguments &amp;args, Clip * &amp;identityClip,
double &amp;identityTime) OVERRIDE FINAL</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This function must return
true if the effect in its current state will not apply any change to
the source image. This is called by the host to determine whether a
call to the render action is necessary or not.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">When true then the
rendering pipeline is much faster as the host just skips this plug-in
from the compositing tree.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The identityClip parameter
must be set to a pointer of the input clip of which the effect is an
identity of.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The identityTime parameter
must be set to the time at which the effect is an identity of the
input clip.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">For example a gain effect
whose &quot;Scale&quot; parameter would be set to 1 would be an
identity.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>The changedParam action</B></P>
<P CLASS="p10" STYLE="margin-bottom: 0.5cm">virtual void
changedParam(const OFX::InstanceChangedArgs &amp;args, const
std::string &amp;paramName) OVERRIDE FINAL;</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This function is called
every time a parameter is changed. This function can be called either
because you set the value of a parameter programmatically or because
the user interacted with the parameter.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">the args.reason parameter
will inform you from what this function was called.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">For example if you had a
button parameter, when the user would press it, it would call this
function with the args.reason == eChangeUserEdit.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This function is also a
great place to show/hide and enable/disable other parameters
according to special values of another parameter.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This function could be
used to implement analysis effects (such as a tracker). Fetching an
image is allowed in this action.</P>
<P CLASS="p5" STYLE="margin-bottom: 0.5cm"><FONT SIZE=1 STYLE="font-size: 8pt"><SPAN STYLE="background: #ffffff">This
is also where parameter writing is supposed to be done. For example
one can fetch an image, find the most popular color and write it in
the parameter, and the next render action will have that parameter
value updated. Data can also be stored in the InstanceData Pointer,
but they would have to be saved during the syncPrivateData action. </SPAN></FONT>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>The
getRegionOfDefinition action</B></P>
<P CLASS="p10" STYLE="margin-bottom: 0.5cm">virtual bool
getRegionOfDefinition(const OFX::RegionOfDefinitionArguments &amp;args,
OfxRectD &amp;rod) OVERRIDE FINAL;</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This is called by the host
to determine the size of the image (or region of definition) produced
by this effect.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">If your plug-in doesn't
apply any geometrical transformation to the image, then it is
probably not modifying it's size (e.g. an Invert plug-in doesn't
modify the image's region of definition.) In that case you do not
need to implement this function, this is the default behaviour to
return the region of definition of the source clip.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">On the other hand, a crop
effect would return the size of the crop area as its region of
definition.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>The
getRegionsOfInterest action</B></P>
<P CLASS="p10" STYLE="margin-bottom: 0.5cm">virtual void
getRegionsOfInterest(const OFX::RegionsOfInterestArguments &amp;args,
OFX::RegionOfInterestSetter &amp;rois) OVERRIDE FINAL;</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Even though the name is
close to the getRegionOfDefinition action, it doesn't serve the same
purpose at all!</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This function is called by
the host before the render action is called. This is called when it
wants to pre-render the input images this effect might need. In order
to do so, the host needs to ask this effect what is the rectangle of
the source image we're interested in. This is the purpose of this
action. In general if your plug-in does need exactly the render
window then you don't have to implement this function. On the other
hand , a blur plug-in might have to include the border padding to the
region of interest of the input clip.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>The getClipPreferences
action</B></P>
<P CLASS="p10" STYLE="margin-bottom: 0.5cm">virtual void
getClipPreferences(ClipPreferencesSetter &amp;clipPreferences)
OVERRIDE FINAL;</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">You rarely need to
implement this action. This action is called by the host to allow the
effect to perform modifications of the state of the clip, such as its
premultiplication (is the image premultiplied or not?) or the image
components (is it alpha, RGB or RGBA?) or image bit depth (is it
byte, short or float ?)</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">For example the shuffle
plug-ins uses it when the users chooses different output components
or different bit depth.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>The render action</B></P>
<P CLASS="p10" STYLE="margin-bottom: 0.5cm">virtual void render(const
OFX::RenderArguments &amp;args) OVERRIDE FINAL;</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This is where the
processing must be done. The args contain several parameters that
define how the image should be rendered.</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">time: The time at which
the render is taking place. This can be used to fetch the input
images at the same time or at other times.</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">renderScale: When
different than 1 this informs that the image is rendered at a lower
resolution than the full resolution. For example this is used in
Natron when the user zooms out. As a filter plug-in , this is merely
an hint and doesn't hold much value. On the other hand for a reader
effect (such as the ones in openfx-io) this is clearly stating at
which scale you should read the image. In this case you should
explicitly downscale the image yourself (ONLY FOR READER PLUG-INS!!).
If you don't support downscaling the images, then in all the actions
called by the host, you're expected to check the render scale
parameter and throw a kOfxStatFailed exception if the render scale is
different than 1. This can in turn inform the host that you don't
support the downscaling of the images and it will take care of it for
you.</P>
<P CLASS="p8" STYLE="margin-bottom: 0.5cm">renderWindow: This is the
portion of the image to render. If you specified that you support
tiles in the describe function of the factory then it might not be
the full <B>region of definition</B> of the image.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">To fetch input images,
call the fetchImage on the source clips at the desired time. You're
expected to check whether the input clip is connected before fetching
the image (i.e: call getConnected() on the clip). A clip is connected
in Natron when the arrow is connected to another effect.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">If you cannot use the
input image for any reason (bad bit depth, bad components, etc…)
then your expected to throw a significant exception to indicate that
the render failed (kOfxErrBadFormat).</P>
<P CLASS="p11" STYLE="margin-bottom: 0.5cm"><B>You can only fetch an
image during the render action or the changedParam action</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Generally a plug-in is
better if it can handle arbitrary bit depths and image components. To
deal with that in our plug-ins we template the internal render
function by the components and the bit depth.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">For example in the Invert
plug-in of the openfx-misc repository, the render function just
instantiate the templated class ImageInverter with the good template
parameters depending on the bit depth and the image components.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In this example we use the
processor object (ImageInverter) to do the rendering because it
enables the multi-threading offered by the host, but we could clearly
do the processing in the render function, though, it wouldn't be
multi-threaded (unless we would have set the
<A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPluginPropHostFrameThreading">kOfxImageEffectPluginPropHostFrameThreading</A>
property to 1).</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT SIZE=3 STYLE="font-size: 13pt"><B>The
getTimeDomain action</B></FONT> 
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT COLOR="#000000"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="background: #ffffff"><FONT FACE="Menlo-Regular">virtual</FONT>
<FONT FACE="Menlo-Regular">bool</FONT> <FONT FACE="Menlo-Regular">getTimeDomain(OfxRangeD</FONT>
<FONT FACE="Menlo-Regular">&amp;range) OVERRIDE</FONT> <FONT FACE="Menlo-Regular">FINAL;</FONT></SPAN></FONT></FONT></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm">This action is called by
the host to figure out what is the frame range over which the plug-in
has an effect. For instance a Reader plug-in would return the length
of the image sequence, or a rectangle generator could return a
firstFrame-lastFrame pair. 
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm">Note that in most cases,
the default implementation is fine, which is to have effect over the
union of ranges of the input effects.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT SIZE=3 STYLE="font-size: 13pt"><B>The
getFramesNeeded action</B></FONT></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT COLOR="#000000"><FONT FACE="Menlo-Regular"><FONT SIZE=1 STYLE="font-size: 8pt">virtual
void getFramesNeeded(const OFX::FramesNeededArguments &amp;args,
OFX::FramesNeededSetter &amp;frames) OVERRIDE FINAL</FONT></FONT></FONT><FONT COLOR="#ffffff"><FONT FACE="Menlo-Regular"><FONT SIZE=2 STYLE="font-size: 11pt">;</FONT></FONT></FONT></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT COLOR="#000000"><FONT FACE="Helvetica, sans-serif"><FONT SIZE=1 STYLE="font-size: 8pt">If
you were to write a plug-in that needs images at different times to
do its processing, e.g. a Retiming plug-in that would need the image
at T – 1 and T + 1 in order to produce the image at frame T, you
need to implement this action.</FONT></FONT></FONT></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT COLOR="#000000"><FONT FACE="Helvetica, sans-serif"><FONT SIZE=1 STYLE="font-size: 8pt">This
is called by the host to figure out what image you will need to
render so it can pre-render them. You should</FONT></FONT></FONT></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT COLOR="#000000"><FONT FACE="Helvetica, sans-serif"><FONT SIZE=1 STYLE="font-size: 8pt">in
turn specify </FONT></FONT><FONT FACE="Helvetica, sans-serif"><FONT SIZE=1 STYLE="font-size: 8pt"><B>exactly</B></FONT></FONT>
<FONT FACE="Helvetica, sans-serif"><FONT SIZE=1 STYLE="font-size: 8pt">what
images will be fetched with the fetchImage(...) function in render. </FONT></FONT></FONT>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><FONT COLOR="#000000"><FONT FACE="Helvetica, sans-serif"><FONT SIZE=1 STYLE="font-size: 8pt">Note
that by default, when implementing a simple effect such as Invert,
you only need the source image at the current time, the default
implementation handles it for you so you don't have to implement it.</FONT></FONT></FONT></P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>The processor</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The host application can
implement the multi-thread suite. Remember, a suite is a set of
functions that a host can implement, offering some functionalities to
the plug-in. In this case this suite is designed to offer SMP style
multi-processing.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The C++ processor class is
just a wrapper around this suite so that it is easier for you, as a
plug-in developer, to multi-thread your processing. You would need to
derive the OFX::ImageProcessor to craft your own processor.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The only function you have
to override is the <B>multiThreadProcessImages </B>function. This is
the core render function which renders an image rectangle for a
single thread. You can cycle through all the examples for inspiration
on how the processing is generally written, though the inner part of
the pixel processing is really up to the plug-in developer.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Bear in mind that it is
more efficient to get all the values from the parameters before
calling the processor multithread function. In order to do that we
generally fetch all the values we want in the render function (or
more specifically in the setupAndProcess function) and then give it
in parameter to the processor class.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The getValue function of a
parameter can be quite expensive and it's better to call it once if
you can.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Same applies for the
fetchImage function.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p6" STYLE="margin-bottom: 0.5cm"><B>Walkthrough: The invert
plug-in</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In this part we will
examine the source code of the <A HREF="https://github.com/NatronGitHub/openfx-misc/tree/master/Invert">Invert
plug-in</A>&nbsp; and comment the relevant parts.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">First, let's take a look
at the <A HREF="https://github.com/NatronGitHub/openfx-misc/blob/master/Invert/PluginRegistration.cpp">PluginRegistration.cpp</A>
file.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Any plug-in must register
in the following function a static factory of the plug-in:</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p12" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">namespace OFX</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">{</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; namespace
Plugin</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
void getPluginIDs(OFX::PluginFactoryArray &amp;ids)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
{</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; ...</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
}</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">}</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In the invert example, we
made a function in the plug-in named getInvertPluginID, declared in
<A HREF="https://github.com/NatronGitHub/openfx-misc/blob/master/Invert/Invert.h">Invert.h</A>
, defined in <A HREF="https://github.com/NatronGitHub/openfx-misc/blob/master/Invert/Invert.cpp">Invert.cpp</A>
and in that function we just instantiate our factory that we declared
above:</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">void
getInvertPluginID(OFX::PluginFactoryArray &amp;ids)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">{</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; static
InvertPluginFactory p(kPluginIdentifier, kPluginVersionMajor,
kPluginVersionMinor);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
ids.push_back(&amp;p);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">}</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The plug-in must be
registered with its raw ID (the plug-in identifier for the host
application) and its version. The host application can then sort the
plug-ins by versions and try to avoid duplicates.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Now that we have that
small function, we need to define the InvertPluginFactory.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p11" STYLE="margin-bottom: 0.5cm"><I><B>The Factory</B></I></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">We declare the class in
the .cpp file, using the handy macro that was created for that
purpose:&nbsp;</P>
<TABLE BORDER=1 CELLPADDING=0 CELLSPACING=0 FRAME=VOID RULES=COLS>
	<TR>
		<TD COLSPAN=2 VALIGN=TOP>
			<P CLASS="p14">mDeclarePluginFactory(InvertPluginFactory, {}, {});</P>
		</TD>
	</TR>
	<TR>
		<TD COLSPAN=2 VALIGN=TOP>
			<P CLASS="p15"><BR>
			</P>
		</TD>
	</TR>
</TABLE>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">That macro just declares
the class and its virtual functions. The virtual functions are the
<B>actions</B> that a host can call on the factory, they are:</P>
<UL>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxActionLoad">The
	load action</A> 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxActionUnload">The
	unload action</A> 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxActionDescribe">The
	describe action</A> 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectActionDescribeInContext">The
	describe in context action</A> 
	</P>
	<LI><P><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxActionCreateInstance">The
	create instance action</A> 
	</P>
</UL>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">#define
mDeclarePluginFactory(CLASS, LOADFUNCDEF, UNLOADFUNCDEF) \</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; class CLASS :
public OFX::PluginFactoryHelper&lt;CLASS&gt; \</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; { \</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; public: \</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; CLASS(const
std::string&amp; id, unsigned int verMaj, unsigned int
verMin):OFX::PluginFactoryHelper&lt;CLASS&gt;(id, verMaj, verMin){} \</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; virtual void
load() LOADFUNCDEF ;\</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; virtual void
unload() UNLOADFUNCDEF ;\</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; virtual void
describe(OFX::ImageEffectDescriptor &amp;desc); \</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; virtual void
describeInContext(OFX::ImageEffectDescriptor &amp;desc,
OFX::ContextEnum context); \</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; virtual
OFX::ImageEffect* createInstance(OfxImageEffectHandle handle,
OFX::ContextEnum context); \</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; };</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">I won't go through the
definition of those actions as the specification does a great job for
that (just click on the links above). The 3 parameters of the factory
declaring macro are the name of the factory, and 2 other arguments
used whether you want to implement the load and unload actions or not.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In the general case you
rarely need to implement those 2 actions, except if you need to do
stuff right after the plug-in is loaded (application launch) and
right before the plug-in is unloaded (application quit).</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">If you don't need to
implement them, then do as the Invert example does, put an empty
brace as parameter, indicating that the definition of the action is
empty.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Otherwise, if you were to
need to implement them, put a semi-colon <B>;</B> as parameter and
then define the functions below.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Let's take a look at the…</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p16" STYLE="margin-bottom: 0.5cm"><B>Create instance
action:&nbsp;</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">OFX::ImageEffect*
InvertPluginFactory::createInstance(OfxImageEffectHandle handle,
OFX::ContextEnum /*context*/)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">{</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; return new
InvertPlugin(handle);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">}</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">All it does is creating an
instance of our plug-in class. We will cover the plug-in class later
on when we're done with the factory.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p16" STYLE="margin-bottom: 0.5cm"><B>The describe action:</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Defines the relevant
properties of our plug-in so that the host can use it in an
appropriate manner:</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">void
InvertPluginFactory::describe(OFX::ImageEffectDescriptor &amp;desc)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">{</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // basic
labels</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.setLabels(kPluginName, kPluginName, kPluginName);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.setPluginGrouping(kPluginGrouping);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.setPluginDescription(kPluginDescription);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // add the
supported contexts</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.addSupportedContext(eContextFilter);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.addSupportedContext(eContextGeneral);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.addSupportedContext(eContextPaint);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // add
supported pixel depths</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.addSupportedBitDepth(eBitDepthUByte);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.addSupportedBitDepth(eBitDepthUShort);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.addSupportedBitDepth(eBitDepthFloat);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // set a
few flags</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.setSingleInstance(false);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.setHostFrameThreading(false);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.setSupportsMultiResolution(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.setSupportsTiles(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.setTemporalClipAccess(false);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.setRenderTwiceAlways(false);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
desc.setSupportsMultipleClipPARs(false);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">}</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The labels of the plug-in
are essentially a duplicate of the name of the plug-in but that is
meant to be seen in different places in the user interface. In our
case we use the plug-in name for all 3 labels
(label,shortLabel,longLabel).</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The plug-in grouping, is
the name of the group in which this plug-in will be found. If you
were to have the grouping &quot;Filter&quot; then in Natron you would
find your plug-in under the Filter menu…</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">You can also specify
subgroups, such as: &quot;MyPluginGroup/Filters&quot;.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The plug-in description is
generally what is seen in the help window the host offers for that
plug-in. In Natron this is what the user sees when clicking on the <B>?</B>
button of the plug-in.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Now we must define the
contexts we can support. You almost always want to support the
general context, this is the context that offers maximum flexibility
to your plug-in. The filter context is essentially different of the
General context</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">because it allows only 1
non optional input clip and doesn't allow masks.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The paint context is
rarely used but just says that if a mask were to be defined, then its
name should not be &quot;Mask&quot; but &quot;Brush&quot;. Some host
application could then in the paint context</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">offer a different use
interface. In Natron however we do not and the paint context is
essentially treated as other contexts.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">As a general rule, try to
support as many contexts as you can, this is cheap to do and offers
maximum portability across all available host applications.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The supported bit depths
of the plug-in is important. This defines what kind of images you
accept in input and what you can output. If you were say to support
only byte (8bit) images then the host application would have to
provide you 8bit input and output images. Note that some hosts (like
Natron) use 32bit floating point images internally, and if you can
this is better to support the highest bit depth. If you can't support
32bit images because the library you're using is 8bit (like OpenCV)
then don't worry, the host application should normally be able to
convert images from different bit depths on its own. You can check
whether the application can do this pixel shuffling by checking the
content of the global</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">ImageEffectHostDescription
gHostDescription;</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This struct offers many
info related to the host currently invoking your plug-in and can be
used to turn on/off special features of your plug-in that work only
if the host application supports it.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Hopefully the support
layer offers means (via templates of the processor class) to support
all bit depths yourself.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The last remaining flags
are very important as they define how the render action is called and
the type of image and render window that should be used.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">I'll just provide links to
the original definition of these flags as they are very well
described in the official spec:</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<UL>
	<LI><P><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPluginPropSingleInstance">kOfxImageEffectPluginPropSingleInstance</A>
	(Defaults to false) 
	</P>
</UL>
<UL>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPluginPropHostFrameThreading">kOfxImageEffectPluginPropHostFrameThreading</A>
	(Defaults to true) 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropSupportsMultiResolution">kOfxImageEffectPropSupportsMultiResolution</A>
	(Defaults to true) 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropSupportsTiles">kOfxImageEffectPropSupportsTiles</A>
	(Defaults to true) 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropTemporalClipAccess">kOfxImageEffectPropTemporalClipAccess</A>
	(Defaults to false) 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPluginPropFieldRenderTwiceAlways">kOfxImageEffectPluginPropFieldRenderTwiceAlways</A>
	(Defaults to true) 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropSupportsMultipleClipPARs">kOfxImageEffectPropSupportsMultipleClipPARs</A>
	(Defaults to false)&nbsp; 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropSupportsMultipleClipDepths">kOfxImageEffectPropSupportsMultipleClipDepths</A>
	(Defaults to false) 
	</P>
	<LI><P><A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPluginRenderThreadSafety">kOfxImageEffectPluginRenderThreadSafety</A>
	(Defaults to instance safe: any instance can have a single &quot;render&quot;
	call at any one time.) 
	</P>
</UL>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The really tricky flags
are:</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">- Multi-resolution: Do you
support input and output images which have different region of
definition ? In this case they can have arbitrary size (different)
and the origin can be something else than 0,0</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">- Tiles: Do you support
render windows that are different than the full region of definition
of an image ? If true then the render window provided as parameter of
the render action can be set to a rectangle&nbsp; smaller than the</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">actual region of
definition of the image.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">-&nbsp; render thread
safety: The default value expects that your plug-in is thread-safe
over several instances. In Natron one instance of your plug-in is a
node. Generally this default value is good enough, unless you have
some dirty global state that you maintain. The best you can do is to
have a fully safe thread-safety, in which case several render threads
can call the render action simultaneously. The host would then call
your render function simultaneously in 2 different cases:</P>
<OL>
	<LI><P STYLE="margin-bottom: 0cm">Because you set the host frame
	threading property to true and you're a fully-safe plug-in. In this
	case the host will slice up the render window by the amount of
	available threads and call as many parallel render as it needs. 
	</P>
	<LI><P>Because your instance is referenced several times in the
	compositing graph and there's several render threads ongoing (for
	example in Natron this would happen if you were to have 2 viewers
	plugged to the same node). 
	</P>
</OL>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Depending on all the
properties that you defined in the describe action, this is of <B>YOUR
RESPONSIBILITY</B> to check in the render action that the image and
arguments provided correspond to all the properties you described.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">If the image doesn't have
a format you can exploit, then fail the render action by throwing the
following exception:</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">OFX::throwSuiteStatusException(kOfxStatErrImageFormat);</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">You can also throw a
kOfxStatFailed exception if the arguments aren't suited to your
plug-in too. This is then the host responsibility to provide you
arguments that are good enough for your plug-in, but catching errors
ensures that your</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">plug-in doesn't crash the
host application because the host did a mistake.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p16" STYLE="margin-bottom: 0.5cm"><B>The describeInContext
action:</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In this function we define
our clips (inputs/output) and the parameters of the plug-in.&nbsp;</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">void
InvertPluginFactory::describeInContext(OFX::ImageEffectDescriptor
&amp;desc, OFX::ContextEnum context)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">{</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // Source
clip only in the filter context</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // create
the mandated source clip</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
ClipDescriptor *srcClip =
desc.defineClip(kOfxImageEffectSimpleSourceClipName);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
srcClip-&gt;addSupportedComponent(ePixelComponentRGBA);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
srcClip-&gt;addSupportedComponent(ePixelComponentRGB);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
srcClip-&gt;addSupportedComponent(ePixelComponentAlpha);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
srcClip-&gt;setTemporalClipAccess(false);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
srcClip-&gt;setSupportsTiles(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
srcClip-&gt;setIsMask(false);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm">&nbsp;&nbsp; &nbsp;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // create
the mandated output clip</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
ClipDescriptor *dstClip =
desc.defineClip(kOfxImageEffectOutputClipName);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
dstClip-&gt;addSupportedComponent(ePixelComponentRGBA);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
dstClip-&gt;addSupportedComponent(ePixelComponentRGB);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
dstClip-&gt;addSupportedComponent(ePixelComponentAlpha);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
dstClip-&gt;setSupportsTiles(true);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; if (context
== eContextGeneral || context == eContextPaint) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
ClipDescriptor *maskClip = context == eContextGeneral ?
desc.defineClip(&quot;Mask&quot;) : desc.defineClip(&quot;Brush&quot;);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
maskClip-&gt;addSupportedComponent(ePixelComponentAlpha);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
maskClip-&gt;setTemporalClipAccess(false);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
if (context == eContextGeneral) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; maskClip-&gt;setOptional(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
}</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
maskClip-&gt;setSupportsTiles(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
maskClip-&gt;setIsMask(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // make
some pages and to things in</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
PageParamDescriptor *page = desc.definePageParam(&quot;Controls&quot;);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
OFX::BooleanParamDescriptor* param =
desc.defineBooleanParam(kParamProcessR);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setLabels(kParamProcessRLabel, kParamProcessRLabel,
kParamProcessRLabel);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setHint(kParamProcessRHint);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setDefault(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setLayoutHint(eLayoutHintNoNewLine);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
page-&gt;addChild(*param);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
OFX::BooleanParamDescriptor* param =
desc.defineBooleanParam(kParamProcessG);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setLabels(kParamProcessGLabel, kParamProcessGLabel,
kParamProcessGLabel);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setHint(kParamProcessGHint);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setDefault(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setLayoutHint(eLayoutHintNoNewLine);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
page-&gt;addChild(*param);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
OFX::BooleanParamDescriptor* param = desc.defineBooleanParam(
kParamProcessB );</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setLabels(kParamProcessBLabel, kParamProcessBLabel,
kParamProcessBLabel);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setHint(kParamProcessBHint);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setDefault(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setLayoutHint(eLayoutHintNoNewLine);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
page-&gt;addChild(*param);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
OFX::BooleanParamDescriptor* param = desc.defineBooleanParam(
kParamProcessA );</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setLabels(kParamProcessALabel, kParamProcessALabel,
kParamProcessALabel);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setHint(kParamProcessAHint);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
param-&gt;setDefault(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
page-&gt;addChild(*param);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
ofxsPremultDescribeParams(desc, page);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
ofxsMaskMixDescribeParams(desc, page);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">}</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">First we define our clips.
Your plug-in <B>MUST</B> have an output clip. Then depending in which
context the describe action is called, you can define one or more
input clips.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The input clips in Natron
are seen from left to right in reverse order of the one you declared
your clips. That is if you define your clip &quot;MyInput1&quot; and
&quot;MyInput2&quot; in that order, Natron</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">would then instantiate the
node this way:</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; MyInput2&nbsp;
&nbsp; MyInput1</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
\ /</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">\ /</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">----------------------------</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">| |</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">| MyEffect |</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">| |</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">----------------------------</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">If you're in the general
context or paint context your effect can then also have a mask clip.
Generally the mask is a clip that only supports alpha images. Don't
forget to specify whether a clip is optional or not (an optional clip
is then not mandatory to render) and to define which components your
clip support.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">After that you would
basically declare all the parameters.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Generally you define a
page parameter which will contain some other children parameters. A
page parameter in Natron is represented as a tab in the settings
panel of the node.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">You can also create
subgroups of parameters by defining a group parameter.&nbsp;</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">A parameter should always
belong to a page. If you fail to put it into a page, then Natron by
default will put it in some default tab (generally the &quot;Node&quot;
tab).</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Each parameter have a
label (the visible label on the left hand side of the parameter in
the settings panel) and a script name. This is important to have some
sort of standard for naming.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Otherwise the user ends-up
with a poorly aligned user interface and more importantly the
scripting of the application gets messier because it is not easy to
reference parameters in a script which have</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">space into their name!</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Generally we use the
following convention for naming parameters: (this is what can be used
in a script)</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">myParameter1</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">myLongParameterWithMoreThan1Word</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">And this convention for
naming labels: (this is what is seen in the user interface)</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">My parameter 1</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">My long parameter with
more than 1 word</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">We also define macros for
all these strings in the beginning of the .cpp file so that it makes
it easier to make quick changes.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The hint of a parameter is
the string that will be displayed in the tooltip when the user hovers
with the mouse the parameter.&nbsp;</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">You should always set the
default value for your parameter.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Almost all parameters
animates by default: In Natron they will have the animation button on
the right hand side. If you want to disable animation, you need to
explicitly set the animation disabled.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The following&nbsp;
parameters on the other hand do not animate by default :
String,Boolean,Choice, you need to explicitly enable animation on
them if you want them to animate.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The layout hint is a hint
to the application as to whether the parameters should be on the same
line or not. By default a new parameter makes a new line.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">We're now ready and set to
talk about the plug-in class...</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p11" STYLE="margin-bottom: 0.5cm"><I><B>The plug-in</B></I></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Your class should inherit
the OFX::ImageEffect class which gives you access to a bunch of
virtual functions to implement. Those functions represent the actions
the host can call on your plug-in.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">All actions of the plug-in
have a default behaviour except the render action and generally you
don't need to implement all actions.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In the Invert plug-in, we
only needed to implement 3 actions:</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">- The render action</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">- The isIdentity action</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">- the changed clip action</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p16" STYLE="margin-bottom: 0.5cm"><B>The constructor:</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">InvertPlugin(OfxImageEffectHandle
handle)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; : ImageEffect(handle)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; , dstClip_(0)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; , srcClip_(0)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
dstClip_ = fetchClip(kOfxImageEffectOutputClipName);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
assert(dstClip_ &amp;&amp; (dstClip_-&gt;getPixelComponents() ==
ePixelComponentRGB || dstClip_-&gt;getPixelComponents() ==
ePixelComponentRGBA || dstClip_-&gt;getPixelComponents() ==
ePixelComponentAlpha));</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
srcClip_ = fetchClip(kOfxImageEffectSimpleSourceClipName);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
assert(srcClip_ &amp;&amp; (srcClip_-&gt;getPixelComponents() ==
ePixelComponentRGB || srcClip_-&gt;getPixelComponents() ==
ePixelComponentRGBA || srcClip_-&gt;getPixelComponents() ==
ePixelComponentAlpha));</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
maskClip_ = getContext() == OFX::eContextFilter ? NULL :
fetchClip(getContext() == OFX::eContextPaint ? &quot;Brush&quot; :
&quot;Mask&quot;);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
assert(!maskClip_ || maskClip_-&gt;getPixelComponents() ==
ePixelComponentAlpha);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
_paramProcessR = fetchBooleanParam(kParamProcessR);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
_paramProcessG = fetchBooleanParam(kParamProcessG);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
_paramProcessB = fetchBooleanParam(kParamProcessB);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
_paramProcessA = fetchBooleanParam(kParamProcessA);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
assert(_paramProcessR &amp;&amp; _paramProcessG &amp;&amp;
_paramProcessB &amp;&amp; _paramProcessA);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
_premult = fetchBooleanParam(kParamPremult);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
_premultChannel = fetchChoiceParam(kParamPremultChannel);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
assert(_premult &amp;&amp; _premultChannel);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
_mix = fetchDoubleParam(kParamMix);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
_maskInvert = fetchBooleanParam(kParamMaskInvert);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
assert(_mix &amp;&amp; _maskInvert);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In the constructor, we
fetch all&nbsp; clips and parameters that we previously defined in
the factory.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">At this point, if the
plug-in would be instantiated from a project saved by a user with
serialised values, all the parameters would already have their value
restored by now. If you do not want a parameter to be persistent (i.e
serialised) then call setIsPersistent(false) on the parameter
descriptor in the describeInContext action.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p16" STYLE="margin-bottom: 0.5cm"><B>The isIdentity action:</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">As a reminder, a plug-in
is identity when it doesn't transform in any way the source image. In
our case if the R,G,B,A parameters are checked off, then our plug-in
doesn't do anything anymore.&nbsp;</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">So in the isIdentity
action, we just check if at least one the parameters is checked. If
not, then we can say we're identity of the input clip at the same
time that was given in parameter.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">bool</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">InvertPlugin::isIdentity(const
IsIdentityArguments &amp;args, Clip * &amp;identityClip, double
&amp;/*identityTime*/)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">{</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; bool red,
green, blue, alpha;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; double mix;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_paramProcessR-&gt;getValueAtTime(args.time, red);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_paramProcessG-&gt;getValueAtTime(args.time, green);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_paramProcessB-&gt;getValueAtTime(args.time, blue);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_paramProcessA-&gt;getValueAtTime(args.time, alpha);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_mix-&gt;getValueAtTime(args.time, mix);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; if (mix ==
0. || (!red &amp;&amp; !green &amp;&amp; !blue &amp;&amp; !alpha)) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
identityClip = srcClip_;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
return true;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; } else {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
return false;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">}</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Note here that we use the
getValueAtTime() function on the parameters and not the getValue()
function. We do this because those parameter animate and we want to
fetch their exact value at the time given in parameter of the action.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">If you were to call
getValue() only, then the host would also call getValueAtTime() but
it would take some overhead because it would need to also fetch the
current time at which you called the function.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">So as a general rule of
thumbs:&nbsp;</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">- Call getValueAtTime for
parameters that animate</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">- Call getValue for all
parameters that do not animate</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p16" STYLE="margin-bottom: 0.5cm"><B>The changedClip
action:</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">void</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">InvertPlugin::changedClip(const
InstanceChangedArgs &amp;args, const std::string &amp;clipName)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">{</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; if
(clipName == kOfxImageEffectSimpleSourceClipName &amp;&amp; srcClip_
&amp;&amp; args.reason == OFX::eChangeUserEdit) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
switch (srcClip_-&gt;getPreMultiplication()) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case eImageOpaque:</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case eImagePreMultiplied:</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; _premult-&gt;setValue(true);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case eImageUnPreMultiplied:</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; _premult-&gt;setValue(false);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
}</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">}</P>
<P CLASS="p12" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In the Invert plug-in we
have a special parameter that can be checked to unpremultiply the
colour channels (RGB) by the Alpha channel before inverting the
image.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This action is called when
the user changes a connection of the plug-in: when the input arrow is
connected to another node, this action handler will be called and
you'll be able to know a bunch of information from the input clip.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In our case we query
whether the input clip is a premultiplied image or not and set the
value of the Unpremultiply parameter according to the input image
state.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p16" STYLE="margin-bottom: 0.5cm"><B>The render action:</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">void</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">InvertPlugin::render(const
OFX::RenderArguments &amp;args)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">{</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; //
instantiate the render code based on the pixel depth of the dst clip</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
OFX::BitDepthEnum &nbsp; &nbsp; &nbsp; dstBitDepth&nbsp; &nbsp; =
dstClip_-&gt;getPixelDepth();</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
OFX::PixelComponentEnum dstComponents&nbsp; =
dstClip_-&gt;getPixelComponents();</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // do the
rendering</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; if
(dstComponents == OFX::ePixelComponentRGBA) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
switch (dstBitDepth) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case OFX::eBitDepthUByte : {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ImageInverter&lt;unsigned char, 4, 255&gt;
fred(*this);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; setupAndProcess(fred, args);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case OFX::eBitDepthUShort : {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ImageInverter&lt;unsigned short, 4,
65535&gt; fred(*this);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; setupAndProcess(fred, args);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case OFX::eBitDepthFloat : {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ImageInverter&lt;float, 4, 1&gt;
fred(*this);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; setupAndProcess(fred, args);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; default :</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp;
OFX::throwSuiteStatusException(kOfxStatErrUnsupported);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
}</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; } else if
(dstComponents == OFX::ePixelComponentRGB) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
switch (dstBitDepth) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case OFX::eBitDepthUByte : {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ImageInverter&lt;unsigned char, 3, 255&gt;
fred(*this);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; setupAndProcess(fred, args);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case OFX::eBitDepthUShort : {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ImageInverter&lt;unsigned short, 3,
65535&gt; fred(*this);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; setupAndProcess(fred, args);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case OFX::eBitDepthFloat : {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ImageInverter&lt;float, 3, 1&gt;
fred(*this);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; setupAndProcess(fred, args);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; default :</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp;
OFX::throwSuiteStatusException(kOfxStatErrUnsupported);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
}</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; } else {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
assert(dstComponents == OFX::ePixelComponentAlpha);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
switch (dstBitDepth) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case OFX::eBitDepthUByte : {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ImageInverter&lt;unsigned char, 1, 255&gt;
fred(*this);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; setupAndProcess(fred, args);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case OFX::eBitDepthUShort : {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ImageInverter&lt;unsigned short, 1,
65535&gt; fred(*this);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; setupAndProcess(fred, args);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; case OFX::eBitDepthFloat : {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ImageInverter&lt;float, 1, 1&gt;
fred(*this);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; setupAndProcess(fred, args);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; default :</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp;
OFX::throwSuiteStatusException(kOfxStatErrUnsupported);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
}</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">}</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In this function we
instantiate the processor that will do the job with the good
parameters.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">We query the bit depth and
the pixel components of the output clip and instantiate the processor
with the template parameters according to the bit depth and the image
components.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">We then can call the
setupAndProcess function which will continue setting up the render.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">void</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">InvertPlugin::setupAndProcess(InvertBase
&amp;processor, const OFX::RenderArguments &amp;args)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">{</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // get a
dst image</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
std::auto_ptr&lt;OFX::Image&gt; dst(dstClip_-&gt;fetchImage(args.time));</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; if
(!dst.get()) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
OFX::throwSuiteStatusException(kOfxStatFailed);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
OFX::BitDepthEnum dstBitDepth &nbsp; &nbsp; &nbsp; =
dst-&gt;getPixelDepth();</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
OFX::PixelComponentEnum dstComponents&nbsp; =
dst-&gt;getPixelComponents();</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // fetch
main input image</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
std::auto_ptr&lt;OFX::Image&gt; src(srcClip_-&gt;fetchImage(args.time));</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // make
sure bit depths are sane</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; if
(src.get()) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
OFX::BitDepthEnum&nbsp; &nbsp; srcBitDepth&nbsp; &nbsp; &nbsp; =
src-&gt;getPixelDepth();</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
OFX::PixelComponentEnum srcComponents = src-&gt;getPixelComponents();</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
// see if they have the same depths and bytes and all</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
if (srcBitDepth != dstBitDepth || srcComponents != dstComponents) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; OFX::throwSuiteStatusException(kOfxStatErrImageFormat);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
}</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // auto ptr
for the mask.</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
std::auto_ptr&lt;OFX::Image&gt; mask((getContext() !=
OFX::eContextFilter) ? maskClip_-&gt;fetchImage(args.time) : 0);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // do we do
masking</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; if
(getContext() != OFX::eContextFilter &amp;&amp;
maskClip_-&gt;isConnected()) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
// say we are masking</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
processor.doMasking(true);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
// Set it in the processor</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
processor.setMaskImg(mask.get());</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; bool red,
green, blue, alpha;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_paramProcessR-&gt;getValueAtTime(args.time, red);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_paramProcessG-&gt;getValueAtTime(args.time, green);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_paramProcessB-&gt;getValueAtTime(args.time, blue);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_paramProcessA-&gt;getValueAtTime(args.time, alpha);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; bool
premult;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; int
premultChannel;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_premult-&gt;getValueAtTime(args.time, premult);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_premultChannel-&gt;getValueAtTime(args.time, premultChannel);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; double mix;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_mix-&gt;getValueAtTime(args.time, mix);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; bool
maskInvert;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
_maskInvert-&gt;getValueAtTime(args.time, maskInvert);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
processor.setValues(red, green, blue, alpha, premult, premultChannel,
mix, maskInvert);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // set the
images</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
processor.setDstImg(dst.get());</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
processor.setSrcImg(src.get());</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // set the
render window</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
processor.setRenderWindow(args.renderWindow);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; // Call the
base class process member, this will call the derived templated
process code</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp;
processor.process();</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">}</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">First thing we do is
fetching the output image in which we will render. If the pointer is
NULL then we fail the render of course, there must be something
terribly wrong in the host application for that to happen.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">We then again fetch the
bit depth and the pixel components of the output image.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Now we fetch the input
image too. One thing we didn't do here but that we should have done
is checking whether the clip is actually connected before fetching
the image. Some hosts return garbage image when the clip is
disconnected.&nbsp;</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In Natron we return a NULL
image, but the plug-in should always check whether a non-optional
input clip is connected before fetching its image.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">If the image returned is
NULL then you can do 2 possible things: render black and transparent
yourself in the processor (that's what we do in the invert plug-in)
or fail the render and the host will probably render black on its
own.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The next thing we do is
checking that the input image and output image bit depths and
components match. Remember that in the describe function we set the&nbsp;
<A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropSupportsMultipleClipPARs">kOfxImageEffectPropSupportsMultipleClipPARs</A></P>
<UL>
	<LI><P>and <A HREF="http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropSupportsMultipleClipDepths">kOfxImageEffectPropSupportsMultipleClipDepths</A>
	properties to false, indicating that we expect the input and output
	images to have the same properties. 
	</P>
</UL>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Now depending on the
context we can also fetch our mask. If the mask is NULL because it is
not connected, this not an issue: this is an optional input and your
processing code should take into account the fact that this mask
might not exist.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">We then fetch all the
values from the parameters that will have effect on the processing
code and pass them to the processor in the setValues(…) function
that we created. getValue() and getValueAtTime() can be expensive,
hence it is better to call them now and once than in the processor
class which will be multi-threaded hence duplicating the API calls.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Don't forget to set the
src and dst image pointers to the processor as well as the render
window. At this point you're ready to call the process() function.
This function will ask the host to launch multiple threads to render
the code inside the processor class. This is a blocking call and will
return only when all threads are finished rendering.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Note that it is <B>FORBIDDEN</B>
to set values of parameters (i.e: calling setValue() and
setValueAtTime()) in the render action. If you were to update some
parameters after your render call then this is not the place to do
so.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Reminder from the spec:</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p20"><I><B>Setting Parameters</B></I></P>
<P CLASS="p21"><I>Plugins are free to set parameters in limited set
of circumstances, typically relating to user interaction. You can
only set parameters in the following actions passed to the plug-in's
main entry function...</I></P>
<UL>
	<LI><P STYLE="margin-bottom: 0cm"><I>The Create Instance Action</I> 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><I>The The Begin Instance Changed
	Action</I> 
	</P>
	<LI><P STYLE="margin-bottom: 0cm"><I>The The Instance Changed Action</I>
		</P>
	<LI><P STYLE="margin-bottom: 0cm"><I>The The End Instance Changed
	Action</I> 
	</P>
	<LI><P><I>The The Sync Private Data Action</I> 
	</P>
</UL>
<UL>
	<LI><P></P>
</UL>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<UL>
	<LI><P><I><B>The processor</B></I> 
	</P>
</UL>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This class is actually
decomposed in 2 classes:&nbsp;</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">- A base class, which is
used by the plug-in in setupAndProcess and avoids templating
everything where we pass the processor in arguments.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This base class only holds
the setters and getters for parameters values and images pointers.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">- A derived class which is
templated by the bit depth (the pixel type here: unsigned
char,unsigned short or float), the number of components and the
maximum value for that bit depth.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">The only relevant function
in the processor is the…</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p16" STYLE="margin-bottom: 0.5cm"><B>multiThreadProcessImages
function:</B></P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In the invert plug-in we
do a special thing here: we template a new function named process()
with the parameters of the plug-in (process red, process green ,
process blue, process alpha). The reason we do that is that it
creates very well</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">optimised code thanks to
the compiler.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">So here the interesting
function in the Invert example is actually the process function….</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">template&lt;bool dored,
bool dogreen, bool doblue, bool doalpha&gt;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; void
process(const OfxRectI&amp; procWindow)</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
float unpPix[4];</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
float tmpPix[4];</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
for (int y = procWindow.y1; y &lt; procWindow.y2; y++) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; if (_effect.abort()) {</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; break;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; PIX *dstPix = (PIX *)
_dstImg-&gt;getPixelAddress(procWindow.x1, y);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; for (int x = procWindow.x1; x &lt; procWindow.x2; x++)
{</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; const PIX *srcPix = (const PIX *)&nbsp;
(_srcImg ? _srcImg-&gt;getPixelAddress(x, y) : 0);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; // do we have a source image to scale up</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ofxsUnPremult&lt;PIX, nComponents,
maxValue&gt;(srcPix, unpPix, _premult, _premultChannel);</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; tmpPix[0] = dored &nbsp; ? (1. -
unpPix[0]) : unpPix[0];</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; tmpPix[1] = dogreen ? (1. - unpPix[1]) :
unpPix[1];</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; tmpPix[2] = doblue&nbsp; ? (1. -
unpPix[2]) : unpPix[2];</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; tmpPix[3] = doalpha ? (1. - unpPix[3]) :
unpPix[3];</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ofxsPremultMaskMixPix&lt;PIX,
nComponents, maxValue, true&gt;(tmpPix, _premult, _premultChannel, x,
y, srcPix, _doMasking, _maskImg, _mix, _maskInvert, dstPix);</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; // increment the dst pixel</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; dstPix += nComponents;</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; &nbsp; &nbsp;
}</P>
<P CLASS="p13" STYLE="margin-bottom: 0.5cm">&nbsp; &nbsp; }</P>
<P CLASS="p17" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This function takes in
parameter for 1 thread what portion of the image it should render
(procWindow). Typically the more threads the end-user computer has,
the smaller the procWindow will be.&nbsp;</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">This function will be
called by the host with new threads&nbsp; it has launched using the
multi-thread suite. That's exactly what does the processor.process()
function in the setupAndProcess code.</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">Make sure that the code in
this function can be well multi-threaded and doesn't require much
synchronisation overhead between the threads, in which case you would
be better off doing that processing directly in the&nbsp;</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">render function without
requiring launching new threads.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">In this example we just
loop over the scan lines first and the over each pixel in a
scan-line.&nbsp;</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">At each loop, we check
whether we should abort processing. If the function abort() returns
true then we must cancel processing; this can be due to user
interaction and generally returning the fastest possible ensures that
the user can have responsive interaction and image renders.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">We then use the
ofxsUnPremult and ofxsPremultMaskMixPix to actually do the
processing. These are functions that we created in the <A HREF="https://github.com/NatronGitHub/openfx-supportext">SupportExt
repository</A>&nbsp; which helps us to write processors.&nbsp; We
created them because most of the processors across different plug-ins
share a good amount of code and factorizing that into unique
functions is easier to maintain. Plus improving one of the function,
improves all plug-ins at once.</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p7" STYLE="margin-bottom: 0.5cm">We're done here for the
walkthrough of the Invert plug-in.&nbsp;</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
<P CLASS="p2" STYLE="margin-bottom: 0.5cm"><BR><BR>
</P>
</BODY>
</HTML>
